package org.lds.hotkey.client.util;

import org.apache.commons.lang3.StringUtils;
import org.lds.hotkey.client.autoconfigure.hotkey.report.ReportService;
import org.lds.hotkey.client.cache.CacheKV;
import org.lds.hotkey.client.cache.HotKeyCache;
import org.lds.hotkey.client.holder.HotKeyHolder;
import org.lds.hotkey.client.holder.RuleHolder;
import org.lds.hotkey.common.autoconfigure.spring.SpringUtil;
import org.lds.hotkey.common.model.HotKeyModel;
import org.lds.hotkey.common.model.ReportRuleModel;
import org.lds.hotkey.common.model.RuleModel;
import org.lds.hotkey.common.util.UUIDUtil;

import java.util.Objects;

/**
 * HotKey工具类
 */
public class HotKeyUtil {
    private static final ReportService REPORT_SERVICE = SpringUtil.getBean(ReportService.class);

    /**
     * 是否是热key
     * @return
     */
    private static boolean isHotKey(String key, boolean report) {
        if (StringUtils.isEmpty(key)) return false;

        // 1. cacheKV
        CacheKV<String, Object> cacheKV = HotKeyCache.getCacheKV(key);
        if (Objects.nonNull(cacheKV)) {
            if (report && cacheKV.isReport() && isNearExpire(cacheKV)) {
                report(cacheKV.getRuleId(), cacheKV.getKey(), true);
            }
            return true;
        }

        // 2. hotkey
        HotKeyModel hotKeyModel = HotKeyHolder.getHotKeyModel(key);
        if (Objects.nonNull(hotKeyModel)) {
            HotKeyCache.put(hotKeyModel.getDuration(), hotKeyModel.getKey(), null);
            return true;
        }

        // 3. rule
        RuleModel ruleModel = RuleHolder.getRuleModel(key);
        if (Objects.nonNull(ruleModel)) {
            report(ruleModel.getId(), key, report);
        }

        return false;
    }

    /**
     * 检查是否是热key 不上报
     * @return
     */
    public static boolean checkHotKey(String key) {
        return isHotKey(key, false);
    }

    /**
     * 是否是热key 自动上报
     * @return
     */
    public static boolean isHotKey(String key) {
        return isHotKey(key, true);
    }

    /**
     * 获取缓存值
     * @param key 热key
     * @param report 是否上报
     * @return
     */
    private static Object getCacheValue(String key, boolean report) {
        if (StringUtils.isEmpty(key)) return null;

        // 1. cacheKV
        CacheKV cacheKV = HotKeyCache.getCacheKV(key);
        if (Objects.nonNull(cacheKV)) return cacheKV.getValue();

        // 2. report
        if (report) {
            // hotkey
            HotKeyModel hotKeyModel = HotKeyHolder.getHotKeyModel(key);
            if (Objects.nonNull(hotKeyModel)) {
                HotKeyCache.put(hotKeyModel.getDuration(), hotKeyModel.getKey(), null);
                return null;
            }

            // rule
            RuleModel ruleModel = RuleHolder.getRuleModel(key);
            if (Objects.nonNull(ruleModel)) {
                report(ruleModel.getId(), ruleModel.getKey(), true);
            }
        }
        return null;
    }

    public static boolean isNearExpire(CacheKV cacheKV) {
        if (cacheKV == null) return true;
        return cacheKV.getTimestamp() + cacheKV.getDuration() * 1000 - System.currentTimeMillis() <= 1000;
    }

    private static void report(Long ruleId, String key, boolean report) {
        if (!report || Objects.isNull(ruleId) || StringUtils.isEmpty(key)) return;

        ReportRuleModel reportRuleModel = ReportRuleModel.builder()
                .uuid(UUIDUtil.simpleUuid())
                .ruleId(ruleId)
                .key(key)
                .build();
        REPORT_SERVICE.report(reportRuleModel);
    }

    public static Object getCacheValue(String key) {
        return getCacheValue(key, false);
    }

    public static Object getCacheValueAllowReport(String key) {
        return getCacheValue(key, true);
    }

    public static boolean put(String key, Object value) {
        if (StringUtils.isEmpty(key) || Objects.isNull(value)) return false;

        // 1. cacheKV
        CacheKV<String, Object> cacheKV = HotKeyCache.getCacheKV(key);
        if (Objects.nonNull(cacheKV)) {
            HotKeyCache.put(cacheKV.getDuration(), cacheKV.getKey(), value, cacheKV.getRuleId());
            return true;
        }

        // 2. hotkey
        HotKeyModel hotKeyModel = HotKeyHolder.getHotKeyModel(key);
        if (Objects.nonNull(hotKeyModel)) {
            HotKeyCache.put(hotKeyModel.getDuration(), hotKeyModel.getKey(), value, cacheKV.getRuleId());
            return true;
        }

        return false;
    }

}
